#pragma once
#include "util.hpp"
#include "mysql.hpp"
#include "online_user.hpp"

#define CHESSROW 15
#define CHESSCOL 15


enum ROOMSTATUS
{
    GAME_RUNNING = 1,
    GAME_OVER
};

enum PIECESTATUS
{
    EMPTY = 0,
    WHITE,
    BLACK
};

class Room
{
private:
    MysqlUserTable *_user_tb;
    OnlineUser *_online_user;
    // 房间状态
    ROOMSTATUS _status;
    // 总玩家数量
    uint32_t _total_count;
    // 黑白子玩家id
    uint32_t _white_id;
    uint32_t _black_id;
    // 房间id
    uint32_t _room_id;

    // 房间棋盘
    std::vector<std::vector<int>> _chessboard;

private:
    bool isFiveInline(int row, int col, int row_step, int col_step, PIECESTATUS color)
    {
        int count = 1;
        int check_row = row + row_step;
        int check_col = col + col_step;
        while(check_row >= 0 && check_row <= CHESSROW 
            &&check_col >= 0 && check_col <= CHESSCOL 
            &&_chessboard[check_row][check_col] == color)
        {
            count++;
            check_row += row_step;
            check_col += col_step;
        }
        check_row = row - row_step;
        check_col = col - col_step;
        while(check_row >= 0 && check_row <= CHESSROW 
            &&check_col >= 0 && check_col <= CHESSCOL 
            &&_chessboard[check_row][check_col] == color)
        {
            count++;
            check_row -= row_step;
            check_col -= col_step;
        }
        if(count >= 5) return true;
        return false;
    }
    bool checkWin(int row, int col, PIECESTATUS color)
    {
        if (isFiveInline(row, col, 0, 1, color) ||
            isFiveInline(row, col, 1, 0, color) ||
            isFiveInline(row, col, 1, 1, color) ||
            isFiveInline(row, col, 1, -1, color))
        {
            return true;
        }
        return false;
    }

public:
    Room(MysqlUserTable *tb, OnlineUser *onlineuser, uint32_t room_id)
        : _user_tb(tb), _online_user(onlineuser), _status(GAME_RUNNING), _room_id(room_id)
        , _total_count(0), _chessboard(CHESSROW, std::vector<int>(CHESSCOL, 0))
    { 
        Log(INFO, "room %d is created", _room_id);
    }
    ~Room()
    { 
        Log(INFO, "room %d is destoryed", _room_id);
    }
    void addWhiter(uint32_t id)
    {
        _white_id = id;
        _total_count++;
    }
    void addBlacker(uint32_t id)
    {
        _black_id = id;
        _total_count++;
    }
    uint32_t getWhiter()
    {
        return _white_id;
    }
    uint32_t getBlacker()
    {
        return _black_id;
    }
    uint32_t getCount()
    {
        return _total_count;
    }
    uint32_t getRoomid()
    {
        return _room_id;
    }
    void handlerChess(const Json::Value& req, Json::Value& response)
    {
        int row = req["row"].asInt();
        int col = req["col"].asInt();
        uint32_t cur_id = req["uid"].asUInt();
        
        //判断对方是否在线
        uint32_t rival_id = cur_id == _white_id ? _black_id : _white_id;
        if(_online_user->findInRoom(rival_id) == false)
        {
            response["room_id"] = req["room_id"].asUInt();
            response["uid"] = req["uid"].asUInt();
            response["row"] = req["row"].asUInt();
            response["col"] = req["col"].asUInt();
            response["reason"] = "对方不在线";
            response["winner"] = cur_id;
            response["result"] = true;
            return;
        }
        //查看是否有棋可以下
        if(_chessboard[row][col] != EMPTY)  //无棋子可下
        {
            response["reason"] = "无棋子可下";
            response["result"] = false;
            return;
        }

        PIECESTATUS cur_color = cur_id == _white_id ? WHITE : BLACK;
        std::cout << row << " " << col << " " << cur_color << std::endl;
        _chessboard[row][col] = cur_color;
        
        for(auto& v : _chessboard)
        {
            for(auto& e : v)
                std::cout << e << " ";
            std::cout << std::endl;
        }

        response["room_id"] = req["room_id"].asUInt();
        response["uid"] = req["uid"].asUInt();
        response["row"] = req["row"].asUInt();
        response["col"] = req["col"].asUInt();
        response["result"] = true;
        response["reason"] = "走棋成功";
        response["winner"] = 0;
        //检测有无胜利
        if(checkWin(row, col, cur_color))
        {
            response["reason"] = "己方五星连珠,战⽆敌";
            response["winner"] = cur_id;
        }
    }
    void handlerChat(const Json::Value& req, Json::Value& response)
    {
        response = req;
        response["result"] = true;
    }
    void handlerRequest(const Json::Value& req)
    {
        Json::Value response;
        std::string type = req["optype"].asString();
        response["optype"] = type;
        //房间号不匹配
        if(_room_id != req["room_id"].asUInt())
        {
            response["result"] = false;
            response["reason"] = "房间号不匹配";
            Log(WARNING, "myroom id %d, req id %d", _room_id, req["room_id"].asUInt());
            
            //不匹配直接广播出去
            broadcast(response);
            return;
        }

        if(type == "put_chess") //下棋模块
        {
            handlerChess(req, response);

            if(response["winner"].asUInt() != 0)
            {
                uint32_t winid = response["winner"].asUInt();
                uint32_t loseid = winid == _white_id ? _black_id : _white_id;

                _user_tb->win(winid);
                _user_tb->lose(loseid);
                _status = GAME_OVER;
                Log(INFO, "Winner appear, the room is %d", _room_id);
            }
        }
        else if(type == "chat") //聊天模块
        {
            handlerChat(req, response);
        }
        else
        {
            response["result"] = false;
            response["reason"] = "请求方法出错";
        }

        broadcast(response);
        return;
    }
    void broadcast(const Json::Value& root)
    {
        std::string response;
        JsonUtil::Serialization(root, response);
        if(_online_user->findInRoom(_white_id))
        {
            websocketServer::connection_ptr wPtr = _online_user->idToConnFromroom(_white_id);
            wPtr->send(response);
            Log(INFO, "send to uid : %d", _white_id);
        }
        if(_online_user->findInRoom(_black_id))
        {
            websocketServer::connection_ptr bPtr = _online_user->idToConnFromroom(_black_id);
            bPtr->send(response);
            Log(INFO, "send to uid : %d", _black_id);
        }
    }
    void handlerExit(uint32_t uid)
    {
        //如果游戏还未结束，一人退出
        if(_status == GAME_RUNNING)
        {
            Json::Value response;
            uint32_t winner_id = uid == _white_id ? _black_id : _white_id;
            response["optype"] = "put_chess";
            response["result"] = true;
            response["room_id"] = _room_id;
            response["reason"] = "对方掉线，不战而胜！";
            response["uid"] = uid;
            response["row"] = -1;
            response["col"] = -1;
            response["winner"] = winner_id;

            _user_tb->win(winner_id);
            _user_tb->lose(uid);
            _status = GAME_OVER;
            broadcast(response);
        }

        _total_count--;
    }
};

typedef std::shared_ptr<Room> roomPtr;

class RoomManage
{
private:
    MysqlUserTable *_user_tb;
    OnlineUser *_online_user;
    std::mutex _mutex;
    uint32_t _next_room_id;
    std::unordered_map<uint32_t, roomPtr> _id_to_room;
    std::unordered_map<uint32_t, uint32_t> _userid_to_roomid;
public:
    RoomManage(MysqlUserTable *tb, OnlineUser *user)
        : _user_tb(tb), _online_user(user), _next_room_id(1)
    { }
    roomPtr createRoom(uint32_t user1, uint32_t user2)
    {
        if(!_online_user->findInHall(user1) || !_online_user->findInHall(user2))
        {
            Log(WARNING, "create room fail, one user not in hall");
            return nullptr;
        }

        std::unique_lock<std::mutex> lock(_mutex);
        roomPtr newroom(new Room(_user_tb, _online_user, _next_room_id));
        newroom->addWhiter(user1);
        newroom->addBlacker(user2);
        _id_to_room.insert(std::make_pair(_next_room_id, newroom));
        _userid_to_roomid.insert(std::make_pair(user1, _next_room_id));
        _userid_to_roomid.insert(std::make_pair(user2, _next_room_id));
        _next_room_id++;

        return newroom;
    }
    //通过房间号找房间
    roomPtr findRoomFromRid(uint32_t rid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto iter = _id_to_room.find(rid);
        if(iter == _id_to_room.end())
        {
            Log(WARNING, "This room number cannot be found");
            return nullptr;
        }
        return iter->second;
    }
    uint32_t findRidFromUid(uint32_t uid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto iter = _userid_to_roomid.find(uid);
        if(iter == _userid_to_roomid.end())
        {
            Log(WARNING, "This user id cannot be found");
            //找不到返回0
            return 0;
        }
        return iter->second;
    }
    //通过用户id找房间
    roomPtr findRoomFromUid(uint32_t uid)
    {
        uint32_t rid = findRidFromUid(uid);
        return findRoomFromRid(rid);
    }
    void removeRoom(uint32_t rid)
    {
        roomPtr room = findRoomFromRid(rid);
        if(room == nullptr)
        {
            Log(WARNING, "This room number cannot be found");
            return;
        }
        std::unique_lock<std::mutex> lock(_mutex);
        uint32_t uid1 = room->getWhiter();
        uint32_t uid2 = room->getBlacker();
        _userid_to_roomid.erase(uid1);
        _userid_to_roomid.erase(uid2);
        _id_to_room.erase(rid);
    }
    void removeRoomUser(uint32_t uid)
    {
        roomPtr room = findRoomFromUid(uid);
        if(room == nullptr)
        {
            Log(WARNING, "This room number cannot be found");
            return;
        }
        room->handlerExit(uid);
        if(room->getCount() == 0)
        {
            removeRoom(room->getRoomid());
        }
    }
};